package com.suke.czx.modules.goods.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.suke.czx.common.utils.*;
import com.suke.czx.modules.goods.entity.Goods;
import com.suke.czx.modules.goods.entity.GoodsSearchBo;
import com.suke.czx.modules.goods.mapper.GoodsMapper;
import com.suke.czx.modules.goods.service.GoodsService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用模块名称: 物品
 * 代码描述: 物品业务层
 * copyright: AUZAR ALL RIGHTS RESERVED
 * company: AUZAR
 *
 * @author: 颜米晨
 * @date: 2021/09/03 15:19:57
 */
@Service
@AllArgsConstructor
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService {

    private final GoodsMapper goodsMapper;

    /**
     * 查询物品列表
     * @param goodsSearchBo 查询BO
     * @return 物品数据集
     */
    @Override
    public R selectGoodsList(GoodsSearchBo goodsSearchBo) {
        switch (goodsSearchBo.getType()) {
            // 如果是经营管理清单
            case CodeConstant.GOODS_TYPE_MANAGEMENT:
                return R.ok().setData(goodsMapper.selectGoodsList(goodsSearchBo));
            // 如果是计划采购清单
            case CodeConstant.GOODS_TYPE_PLAN:
                if (goodsSearchBo.getProjectId() == null) {
                    return R.error("查询计划采购清单,项目编码不能为空");
                }
                return R.ok().setData(goodsMapper.selectGoodsList(goodsSearchBo));
            // 如果是项目合同清单
            case CodeConstant.GOODS_TYPE_CONTRACT:
            // 如果是实际采购清单
            case CodeConstant.GOODS_TYPE_ACTUAL:
                return R.ok().setData(goodsMapper.selectContractGoodsList(goodsSearchBo));
            // 如果是实际采购清单合并对应的交付清单
            case CodeConstant.GOODS_TYPE_ACTUAL_AND_PAY:
                // 变更物品类型为实际采购清单
                goodsSearchBo.setType(CodeConstant.GOODS_TYPE_ACTUAL);
                List<Object> actualAndPayList = new ArrayList<>();
                List<Goods> actualList = goodsMapper.selectContractGoodsList(goodsSearchBo);
                if (!ListUtil.isEmpty(actualList)) {
                    // 查询关联交付清单物品map
                    Map<Long, Goods> relationPayGoodsMap = goodsMapper.selectRelationPayGoodsMap(goodsSearchBo, actualList.stream()
                            .map(Goods::getGoodsId).collect(Collectors.toList()));
                    actualList.forEach(actual -> {
                        Map<Integer, Goods> actualAndPayMap = new HashMap<>(4);
                        actualAndPayMap.put(CodeConstant.GOODS_TYPE_ACTUAL, actual);
                        actualAndPayMap.put(CodeConstant.GOODS_TYPE_PAY, relationPayGoodsMap.get(actual.getGoodsId()));
                        actualAndPayList.add(actualAndPayMap);
                    });
                } else {
                    actualList.forEach(actual -> {
                        Map<Integer, Goods> actualAndPayMap = new HashMap<>(4);
                        actualAndPayMap.put(CodeConstant.GOODS_TYPE_ACTUAL, actual);
                        actualAndPayList.add(actualAndPayMap);
                    });
                }
                return R.ok().setData(actualAndPayList);
            default:
                break;
        }
        return R.ok().setData(new ArrayList<>());
    }

    /**
     * 保存物品
     * @param goods 物品
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveGoods(Goods goods) {
        switch (goods.getType()) {
            // 如果是经营管理清单直接保存
            case CodeConstant.GOODS_TYPE_MANAGEMENT:
            // 如果是计划采购清单直接保存
            case CodeConstant.GOODS_TYPE_PLAN:
                save(goods);
                return R.ok();
            // 如果是项目合同清单需要提供合同编码
            case CodeConstant.GOODS_TYPE_CONTRACT:
            // 如果是实际采购清单需要提供合同编码
            case CodeConstant.GOODS_TYPE_ACTUAL:
                if (goods.getContractId() == null) {
                    return R.error("合同编码不能为空");
                } else {
                    save(goods);
                    // 新增实际采购清单的同时生成1条对应的交付清单记录且交付数量为0
                    Goods pay = new Goods();
                    BeanUtil.copyProperties(goods, pay);
                    // 设置数量为0,relationId为对应实际采购清单的物品id,类型为交付清单类型
                    pay.setCount(BigDecimal.ZERO);
                    pay.setRelationId(goods.getGoodsId());
                    pay.setType(CodeConstant.GOODS_TYPE_PAY);
                    save(pay);
                    return R.ok();
                }
            default:
                return R.error("物品类型错误");
        }
    }

    /**
     * 更新物品
     * @param goods 物品
     * @return VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateGoods(Goods goods) {
        switch (goods.getType()) {
            // 如果是经营管理清单直接更新
            case CodeConstant.GOODS_TYPE_MANAGEMENT:
            // 如果是计划采购清单直接更新
            case CodeConstant.GOODS_TYPE_PLAN:
                updateById(goods);
                return R.ok();
            // 如果是项目合同清单需要提供合同编码
            case CodeConstant.GOODS_TYPE_CONTRACT:
                if (goods.getContractId() == null) {
                    return R.error("合同编码不能为空");
                } else {
                    updateById(goods);
                    return R.ok();
                }
            // 如果是实际采购清单需要提供合同编码并且如果存在对应的交付清单则物品数量不可以比交付清单物品数量小
            case CodeConstant.GOODS_TYPE_ACTUAL:
                if (goods.getContractId() == null) {
                    return R.error("合同编码不能为空");
                } else {
                    // 查询是否有对应的交付清单记录
                    Goods pay = getOne(Wrappers.<Goods>query().eq(FieldConstant.DEL_FLG, CodeConstant.NOT_DEL)
                            .eq(StrUtil.toUnderlineCase(FieldConstant.RELATION_ID), goods.getGoodsId()));
                    if (pay != null && goods.getCount().compareTo(pay.getCount()) < 0) {
                        return R.error("已有交付记录,修改数量不能比交付的数量少,否则请先修改对应交付记录的数量");
                    } else {
                        updateById(goods);
                        return R.ok();
                    }
                }
            // 如果是交付清单则必须验证是否有对应的实际采购清单并且交付清单可修改的数量不能大于实际采购清单
            case CodeConstant.GOODS_TYPE_PAY:
                if (goods.getRelationId() == null) {
                    return R.error("交付清单记录需要有对应的实际采购清单记录关系编码");
                }
                Goods actual = getOne(Wrappers.<Goods>query().eq(FieldConstant.DEL_FLG, CodeConstant.NOT_DEL)
                        .eq(StrUtil.toUnderlineCase(FieldConstant.GOODS_ID), goods.getRelationId()));
                if (actual == null) {
                    return R.error("交付清单记录需要有对应的实际采购清单记录");
                }
                if (goods.getCount().compareTo(actual.getCount()) > 0) {
                    return R.error("已有实际清单记录,修改数量不能比实际清单记录数量多,否则请先修改对应实际清单记录的数量");
                } else {
                    updateById(goods);
                    return R.ok();
                }
            default:
                return R.error("物品类型错误");
        }
    }

    /**
     * 删除物品
     * @param goodsId 物品编码
     * @param userId 操作者编码
     * @return VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateDelGoods(Long goodsId, Long userId) {
        // 判断该记录是否存在
        Goods goods = getOne(Wrappers.<Goods>query().eq(FieldConstant.DEL_FLG, CodeConstant.NOT_DEL)
                .eq(StrUtil.toUnderlineCase(FieldConstant.GOODS_ID), goodsId));
        if (goods == null) {
            return R.error("该物品记录不存在");
        }
        switch (goods.getType()) {
            // 如果是经营管理清单直接删除
            case CodeConstant.GOODS_TYPE_MANAGEMENT:
            // 如果是项目合同清单直接删除
            case CodeConstant.GOODS_TYPE_CONTRACT:
            // 如果是计划采购清单直接删除
            case CodeConstant.GOODS_TYPE_PLAN:
            // 如果是交付清单直接删除
            case CodeConstant.GOODS_TYPE_PAY:
                goodsMapper.updateDelGoods(goodsId, userId);
                return R.ok();
            // 如果是实际采购清单需要判断是否有对应交付清单记录
            case CodeConstant.GOODS_TYPE_ACTUAL:
                Goods pay = getOne(Wrappers.<Goods>query().eq(FieldConstant.DEL_FLG, CodeConstant.NOT_DEL)
                        .eq(StrUtil.toUnderlineCase(FieldConstant.RELATION_ID), goodsId));
                if (pay != null) {
                    return R.error("该实际采购清单记录存在对应交付清单记录,请先删除对应交付清单记录");
                } else {
                    goodsMapper.updateDelGoods(goodsId, userId);
                    return R.ok();
                }
            default:
                return R.error("物品类型错误");
        }
    }

    /**
     * 同步项目合同清单
     * @param projectId 项目编码
     * @param userId 操作者编码
     * @return VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R sync(Long projectId, Long userId) {
        // 查询是否有项目合同清单物品可供同步
        List<Goods> contractGoodsList = goodsMapper.selectGoodsTypeContract(projectId);
        if (ListUtil.isEmpty(contractGoodsList)) {
            return R.error("没有可供同步的项目合同清单物品");
        }
        // 查询项目合同清单物品是否已有过同步操作
        List<Goods> planGoodsList = goodsMapper.selectHasSyncGoodsTypePlan(contractGoodsList.stream()
                .map(Goods::getGoodsId).collect(Collectors.toList()));
        if (!ListUtil.isEmpty(planGoodsList)) {
            String hasSyncGoods = planGoodsList.stream().map(Goods::getGoodsName).collect(Collectors.joining(Constant.COMMA));
            return R.error(hasSyncGoods + "已经同步，请撤销后重新执行同步操作");
        }
        // 遍历执行同步操作
        contractGoodsList.forEach(g -> {
            // 设置关联id
            g.setRelationId(g.getGoodsId());
            // 设置物品类型
            g.setType(CodeConstant.GOODS_TYPE_PLAN);
            // 置空物品编码
            g.setGoodsId(null);
            // 置空合同编码
            g.setContractId(null);
            g.setCreator(userId);
            g.setCreateTime(new Date());
            // 同步计划采购清物品
            save(g);
        });
        return R.ok();
    }

    /**
     * 撤销同步项目合同清单
     * @param projectId 项目编码
     * @param userId 操作者编码
     * @return VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R delSync(Long projectId, Long userId) {
        // 查询是否有计划采购清单物品可撤销同步
        List<Goods> planGoodsList = goodsMapper.selectGoodsTypePlan(projectId);
        if (ListUtil.isEmpty(planGoodsList)) {
            return R.error("没有可供撤销同步的计划采购清单物品");
        }
        planGoodsList.forEach(g -> updateDelGoods(g.getGoodsId(), userId));
        return R.ok();
    }

    /**
     * 批量交付物品
     * @param goodsList 项目编码
     * @param userId 操作者编码
     * @return VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R batchDeliver(List<Goods> goodsList, Long userId) {
        // 判断批量交付清单是否为空
        if (ListUtil.isEmpty(goodsList)) {
            return R.error("批量交付清单为空");
        }
        // 更新对应的交付清单记录
        goodsList.stream().peek(g -> {
            g.setType(CodeConstant.GOODS_TYPE_PAY);
            g.setUpdater(userId);
            g.setUpdateTime(new Date());
        }).forEach(this::updateGoods);
        return R.ok();
    }

}
